<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "https://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="zh-CN" />
<!--[if lt IE 9]><style type="text/css">@font-face { font-family: "JinBuGuoWebMono"; src: url("/d/mono.eot"); }</style><![endif]-->
<style media="all" type="text/css">
@font-face { font-family: "JinBuGuoWebMono"; src: url("http://www.jinbuguo.com/d/mono.ttf") format("truetype"); }
* { font-family: "JinBuGuoWebMono", "Ubuntu Mono", "Consolas", "Menlo", monospace; }
body { margin:10px; }
h1,h2 { text-align:center; background:#ddd; }
h2 { margin: 10px 5%; }
h2#auth_name {  background:#fff; }
dt { margin-top: 0.5em; color:#600; }
pre { background: #edc; }
</style>
<title>简单、严谨、安全 —— Dcron 简介 [金步国]</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head>

<body>
<h1>简单、严谨、安全 —— Dcron 简介</h1><h2 id="auth_name">作者：<strong><a href="../index.html">金步国</a></strong></h2>
<hr />
<h3>版权声明</h3>
<p>本文作者是一位开源理念的坚定支持者，所以本文虽然不是软件，但是遵照开源的精神发布。</p>
<ul>
<li>无担保：本文作者不保证作品内容准确无误，亦不承担任何由于使用此文档所导致的损失。</li>
<li>自由使用：任何人都可以自由的<u>阅读/链接/打印</u>此文档，无需任何附加条件。</li>
<li>名誉权：任何人都可以自由的<u>转载/引用/再创作</u>此文档，但必须保留作者署名并注明出处。</li>
</ul>
<h3>其他作品</h3>
<p>本文作者十分愿意与他人分享劳动成果，如果你对我的其他翻译作品或者技术文章有兴趣，可以在如下位置查看现有的作品集：</p>
<ul>
<li><a href="../index.html">金步国作品集</a> [ <a href="../index.html">http://www.jinbuguo.com/</a> ]</li>
</ul>
<h3>联系方式</h3>
<p>由于作者水平有限，因此不能保证作品内容准确无误。如果你发现了作品中的错误(哪怕是错别字也好)，请来信指出，任何提高作品质量的建议我都将虚心接纳。</p>
<ul>
<li>Email(QQ)：70171448在QQ邮箱</li>
</ul>
<hr />



<h2>计划任务</h2>
<p>在 UNIX 操作系统中，通常都有一个 crond 守护进程来定期执行设定好的计划任务，它能给服务器管理带来许多便利，同时可以大大节省管理员的时间和精力。比如，管理员希望每天凌晨 3:00 对数据库进行一次备份，有了 crond 的帮助，管理员就不必每天在睡得最香的时候起床了，crond 能够按照配置文件的指引自动完成这个任务。</p>
<p>在GNU的世界里，最不缺乏的就是选择，同样，这个 crond 也有多种不同的实现，常见的有：<a href="http://fcron.free.fr/">fcron</a> 、<a href="http://www.jimpryor.net/linux/dcron.html">dcron</a> 、<a href="http://ftp.isc.org/isc/cron/">vixie-cron</a> 三种。本文将介绍其中的 dcron 实现，它的特点就是“简单、严谨、安全”。</p>

<h2>Dcron</h2>
<p>Dcron 3.2 版本以前是 Dillon 的作品，现在已经交给 Jim Pryor 维护，其主页是：<a href="http://www.jimpryor.net/linux/dcron.html">http://www.jimpryor.net/linux/dcron.html</a></p>
<p>Dcron 将所有任务都强制交给 /bin/sh 执行，也就是使用"/bin/sh -c"来运行任务。Dcron 不允许你在配置文件中指定环境变量或使用其它高级特性，这些事情最好放到脚本中去做。</p>
<p>Dcron 启动后首先扫描普通用户配置目录，寻找以/etc/passwd中用户名命名的文件，并将其作为该用户的配置文件。然后扫描root用户配置目录，并将其下所有文件都作为root用户的配置文件[笔者本人建议不要使用root配置目录，而是在普通用户配置目录中放置一个"root"文件，效果完全相同]。[注意]配置目录下所有文件名含有句点(.)的文件和无法匹配用户名的文件都会被忽略。</p>
<p>Dcron 在运行时会每分钟检查一次配置文件目录中的"cron.update"文件。你可以将已经被修改或删除的配置文件的名字(也就是用户名)添加到"cron.update"文件中(一行一个)，这样就可以通知 Dcron 配置文件已经发生变化。不过，即使没有"cron.update"文件，Dcron 也会每小时自动重新扫描一次配置目录。</p>
<p>Dcron 有许多内置的限制以防止其被误用。其中最重要的是：每个配置文件最大256行规则。每行规则最长1020字符。</p>
<p>Dcron 并不使用 Sysklogd 记录日志，一般的方法是直接将命令的结果重定向到日志文件。</p>
<p>Dcron 的执行细节：它必须至少运行一个任务。它首先创建两个进程自己拥有的临时文件 O_EXCL 和 O_APPEND 存储所有输出，然后 fork() 出子进程并将其身份切换到该任务的属主，然后使用 exec 函数执行"/bin/sh -c"来运行任务，但是 O_EXCL 和 O_APPEND 始终由守护进程所有，以防止被用户恶意修改。任务完成后 crond 会校验没有安全防护的 mail 文件，如果它被新增了内容，那么就以任务属主的身份使用 sendmail 将修改过的 mail 文件发送给其属主。crond 不会在任务执行期间保持打开文件描述符，因为这样做可能会导致文件描述符被耗尽。crond 总是在每一分钟的开始时刻检查当前是否有事情可做，这种检查的效率很高，几乎不消耗任何 CPU 资源。</p>
<p>如果轮到某一条命令执行的时候，先前启动的命令尚未执行结束，那么将跳过此次执行。比如，如果设定每分钟执行一次"sleep 70"，实际效果将是每两分钟执行一次。如果你不喜欢这样，可以在命令之后加上"&"转入后台执行，这样就不会受此影响了。</p>

<h2>编译与安装</h2>
<p>Dcron 是一个标准的 ANSI C 程序，理论上可以使用任何符合标准的编译器进行编译(推荐使用 GCC)。但事实上，源代码中切换用户身份的操作是特定于Linux的方式，所以，只建议在Linux上编译和安装。</p>
<p>编译之前你需要详细阅读 Makefile 和 defs.h 文件，并根据你的需要进行修改。比如作者本人经常进行这样的修改：</p>
<pre>sed -i -e's|/var/spool/cron/crontabs|/etc/crontabs|' -e's|/etc/cron.d|/share/empty|' defs.h</pre>
<p>这个命令的作用是将配置文件的默认目录位置调整到"/etc/crontabs"，并将root用户配置文件的默认目录位置调整到"/share/empty"。</p>
<p>然后使用下面的命令进行编译和安装：</p>
<pre>
make CFLAGS="$CFLAGS $LDFLAGS"
install -o 0 -g 0 -m 0500 crond   /bin/crond
chown 0:0  /etc/crontabs /share/empty
chmod 1700 /etc/crontabs /share/empty
</pre>
<p>请注意在安装时为 crond 及其配置目录设置的权限。</p>
<p> crond 通常在系统启动的时候由 /etc/rc.local 启动。在启动脚本中通常使用"-l8"指定日志等级。由于 Dcron 并不使用 Sysklogd 记录日志，所以一般直接将命令的结果重定向到日志文件。比如：</p>
<pre>/bin/crond -l8 &gt;&gt; /var/log/crond.log 2&gt;&amp;1</pre>

<h2>命令行选项</h2>
<p>语法</p>
<pre>crond [-l#] [-d#] [-f] [-b] [-c directory] [-s directory]</pre>
<p>选项说明</p>
<dl>
<dt>-l&lt;日志等级&gt;</dt><dd>设置日志等级(0-8)，等级越低日志越详细，默认为"8"。</dd>
<dt>-d&lt;调试等级&gt;</dt><dd>设置调试等级，默认为"0"。使用这个选项会同时将日志等级设为"0"，并导致 crond 作为前台进程运行。</dd>
<dt>-f</dt><dd>在前台运行</dd>
<dt>-b</dt><dd>在后台运行。这是默认值，除非你使用了 -d 选项。</dd>
<dt>-c 目录</dt><dd>指定配置文件目录，默认值是 /var/spool/cron/crontabs ，可以在 defs.h 文件中修改这个默认值[我一般修改成 /etc/crontabs]。</dd>
<dt>-s 目录</dt><dd>指定root用户配置文件目录，默认值是 /etc/cron.d ，可以在 defs.h 文件中修改这个默认值[我一般修改成 /etc/crontabs]。</dd>
</dl>

<h2>配置文件语法</h2>
<p>配置文件的格式与 vixie cron 类似，但是缺少一些高级特性，比如不允许在配置文件中使用环境变量。</p>
<p>空白行以及以"#"开头的行将被忽略。</p>
<p>配置文件中的每一行都需要指定5个时间字段+1个命令字段，也就是：</p>
<pre>分钟(0-59)  小时(0-23)  日(1-31)  月(jan-dec)  星期(sun-sat)   命令</pre>
<p>每个字段都可以指定一个范围(比如：feb-nov)，或者逗号分隔的清单(比如：1,5,8,23)，或者星号(*)代表全部。</p>
<p>月份和星期除了数字，还可以使用字符串缩写，具体如下：<br />
1-12月：jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec<br />
周日-周六：sun,mon,tue,wed,thu,fri,sat
</p>
<p>如果你同时指定了每周中的某一天和每月中的某一天，那么两者同时有效，也就是这个任务会在每周的这一天执行，同时也在每月的这一天执行。</p>
<p>下面是几个例子：</p>
<pre>
# 全年每分钟执行一次
*     *     *     *     *      /bin/false

# 每天上午 6:10 执行一次
10    6     *     *     *      date

# 每年 4月2日 22:25 执行一次
25    22    2     apr     *       /bin/true

# 每周一、周二、周三 上午 2:00 6:00 11:00 22:00
# 或者 每月4号 上午 2:00 6:00 11:00 22:00 执行一次
0     2,6,11,22    4     *       mon-wed    date >>/var/log/messages 2>&1
</pre>
<p>命令部分将使用 <pre>/bin/sh -c 命令</pre> 方式执行。</p>
<p>通常将命令的结果重定向到一个日志文件，否则默认将输出到 stdout 或 stderr ，并且同时使用sendmail发送到任务的属主。如果你希望为某些特别的用户(比如 UUCP)利用这个sendmail机制，最好为这个用户设置一个别名以将邮件重定向到 root 或 postmaster 之类的用户。</p>



<hr />

</body></html>